#ifndef BINDING_H
#define BINDING_H
#include "../common/Helper.hpp"
#include <cassert>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <unordered_map>
using namespace std;

// 1. 定义绑定信息类
struct Binding
{
    using ptr = shared_ptr<Binding>;
    Binding() {}
    Binding(const string& bexchange_name, const string& bqueue_name, const string& bbinding_key)
        : exchange_name(bexchange_name), queue_name(bqueue_name), binding_key(bbinding_key)
    {}
    string exchange_name;
    string queue_name;
    string binding_key;
};

// 队列与绑定信息是一一对应的，因此一个交换机可能会有多个队列的绑定信息
// 队列名与绑定信息的映射关系
using QueueBindingMap = unordered_map<string, Binding::ptr>;
// 交换机与队列的映射关系
using BindingMap = unordered_map<string, QueueBindingMap>;

// unordered_map<string, Binding::ptr>; 队列与绑定
// unordered_map<string, Binding::ptr>; 交换机与绑定

// 2. 定义绑定信息持久化
class BindingMapper
{
public:
    BindingMapper(const string& db_file)
        : _sql_helper(db_file)
    {
        string path = FileHelper::parentDirectory(db_file);
        FileHelper::createDirectory(path);
        _sql_helper.open();
        CreateTable();
    }
    void CreateTable()
    {
        // create table if not exists binding_table(exchange_name varchar(32), msgqueue_name, binding_key)
        stringstream sql;
        sql << "create table if not exists binding_table(";
        sql << "exchange_name varchar(32), ";
        sql << "msgqueue_name varchar(32), ";
        sql << "binding_key varchar(128));";
        assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
    }
    void RemoveTable()
    {
        string sql = "drop table if exists binding_table;";
        _sql_helper.exec(sql, nullptr, nullptr);
    }
    bool Insert(Binding::ptr& binding)
    {
        // insert into binding_table values('exchange1', 'msgqueue1', 'news.music.#');
        stringstream sql;
        sql << "insert into binding_table values(";
        sql << "'" << binding->exchange_name << "', ";
        sql << "'" << binding->queue_name << "', ";
        sql << "'" << binding->binding_key << "');";
        return _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void Remove(const string& ename, const string& qname)
    {
        // delete from binding_table where exchange_name='' and msgqueue_name='';
        stringstream sql;
        sql << "delete from binding_table where ";
        sql << "exchange_name='" << ename << "' and ";
        sql << "msgqueue_name='" << qname << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void RemoveExchangeBindings(const string& ename)
    {
        // delete from binding_table where exchange_name='';
        stringstream sql;
        sql << "delete from binding_table where ";
        sql << "exchange_name='" << ename << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void RemoveQueueBindings(const string& qname)
    {
        stringstream sql;
        sql << "delete from binding_table where ";
        sql << "msgqueue_name='" << qname << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    BindingMap Recover()
    {
        BindingMap result;
        // select exchange_name, msgqueue_name, binding_key from binding_table;
        string sql = "select exchange_name, msgqueue_name, binding_key from binding_table;";
        _sql_helper.exec(sql, selectCallback, &result);
        return result;
    }

private:
    static int selectCallback(void* arg,int numcol,char** row,char** fields) {
        BindingMap* result = (BindingMap*)arg;
        Binding::ptr bp = make_shared<Binding>(row[0], row[1], row[2]);
        //为了防止 交换机相关的绑定信息已经存在，因此不能直接创建队列映射，进行添加，这样会覆盖历史数据
        //因此得先获取交换机对应的映射对象，往里边添加数据
        //但是，若这时候没有交换机对应的映射信息，因此这里的获取要使用引用（会保证不存在则自动创建）
        QueueBindingMap &qmap = (*result)[bp->exchange_name];
        qmap.insert(make_pair(bp->queue_name, bp));
        return 0;
    }
private:
    SqliteHelper _sql_helper;
};

// 3. 绑定信息管理模块
class BindingManager
{
public:
    using ptr = std::shared_ptr<BindingManager>;
    BindingManager(const string& dbfile) 
        : _mapper(dbfile)
    {
        _bindings = _mapper.Recover();
    }
    
    bool bind(const string& ename, const string& qname, const string& key, bool durable)
    {
        // 构造队列绑定信息对象，添加映射关系
        unique_lock<mutex> lock(_mutex);
        auto it = _bindings.find(ename);
        if(it != _bindings.end() && it->second.find(qname) != it->second.end())
            return true;
        Binding::ptr bp = make_shared<Binding>(ename, qname, key);
        if(durable)
        {
            bool ret = _mapper.Insert(bp);
            if(ret == false)
                return false;
        }
        // 把队列拿出来
        auto& qbmap = _bindings[ename];
        qbmap.insert(make_pair(qname, bp));
        return true;
    }
    void unbind(const string& ename, const string& qname)
    {
        // 先得有才能解除绑定
        unique_lock<mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if(eit == _bindings.end())
            return;
        auto qit = eit->second.find(qname);
        if(qit == eit->second.end())
            return;
        // 都有 就删除
        _mapper.Remove(ename, qname);
        _bindings[ename].erase(qname);
    }
    void RemoveExchangeBindings(const string &ename) 
    {
        unique_lock<mutex> lock(_mutex);
        _mapper.RemoveExchangeBindings(ename);
        _bindings.erase(ename);
    }
    void RemoveQueueBindings(const string &qname) 
    {
        unique_lock<mutex> lock(_mutex);
        _mapper.RemoveQueueBindings(qname);
        for (auto start = _bindings.begin(); start != _bindings.end(); ++start) 
        {
            //遍历每个交换机的绑定信息，从中移除指定队列的相关信息
            start->second.erase(qname);
        }
    }

    Binding::ptr GetBinding(const string& ename, const string& qname)
    {
        unique_lock<mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end())
            return Binding::ptr(); 
        auto qit = eit->second.find(qname);
        if (qit == eit->second.end())
            return Binding::ptr(); 
        return qit->second;
    }

    QueueBindingMap getExchangeBindings(const string &ename) 
    {
        unique_lock<mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end())
            return QueueBindingMap();
        return eit->second;
    }

    bool exists(const string &ename, const string &qname) 
    {
        unique_lock<mutex> lock(_mutex);
        auto eit = _bindings.find(ename);
        if (eit == _bindings.end())
            return false; 
        auto qit = eit->second.find(qname);
        if (qit == eit->second.end())
            return false;
        return true;
    }
    size_t size() 
    {
        size_t total_size = 0;
        unique_lock<mutex> lock(_mutex);
        for (auto start = _bindings.begin(); start != _bindings.end(); ++start) 
        {
            //遍历每个交换机的绑定信息，从中移除指定队列的相关信息
            total_size += start->second.size();
        }
        return total_size;
    }

    void clear() 
    {
        unique_lock<mutex> lock(_mutex);
        _mapper.RemoveTable();
        _bindings.clear();
    }

private:
    mutex _mutex;
    BindingMapper _mapper;
    BindingMap _bindings;
};
#endif